﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.SharePoint.Administration;
using System.Collections.ObjectModel;

namespace SPALM
{
    internal class RetractAndDeleteSolutionsOperation : DeploymentOperation
    {
        private string Solutions;
        private string WebApplications;   

        private string[] solutionNames;
        private string[] webApplicationNames;

        public RetractAndDeleteSolutionsOperation(Deployment parentTask, string Solutions, string WebApplications, int ParsedRetries, int ParsedWaitSecondsAfterFinish, bool ParsedSimulateDeployment)
        : base(parentTask, ParsedRetries, ParsedWaitSecondsAfterFinish, ParsedSimulateDeployment)
        {            
            // TODO: Complete member initialization
            this.Solutions = Solutions;
            this.WebApplications = WebApplications;

            //Parameter extraction
            solutionNames = GetSolutionNames(Solutions);
            webApplicationNames = GetArrayFromString(WebApplications);
        }

        public override void CheckPreconditions()
        {
            LogMessage(FormatHeader("Check preconditions"), LogLevel.High);
            
            //1. Checks
            if (solutionNames.Length == 0)            
            {
                exceptions.Add("No solutions given in parameter 'Solutions'");
            }

            //b. Existieren die Lösungen bereits auf dem Server
            LogMessage("Check for existing solutions in farm...", LogLevel.Normal);  
            foreach (string solutionName in solutionNames)
            {   LogMessage("Checking '" + solutionName + "'", LogLevel.Low);
                if (!SolutionExistsOnServer(solutionName))
                {
                    LogWarning("Solution '" + solutionName + "' does not exists on server.");
                }
            }

            //Existieren die angegebenen Webapps
            if (webApplicationNames.Length > 0)
            {
              LogMessage("Check existing web applications...", LogLevel.Normal);
              foreach (string webAppName in webApplicationNames)
              {
                LogMessage("Checking web application '" + webAppName + "'", LogLevel.Low);
                if (!WebApplicationExistsOnServer(webAppName))
                {
                  exceptions.Add("Web application '" + webAppName + "' does not exists on server.");
                }
              }
            }

            LogMessage(FormatHeader("Check preconditions finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High);
        }

        public override void PrepareSystem()
        {
            //LogMessage("Prepare system for Retract Solutions", LogLevel.High);            
        }

        public override void RunOperation()
        {
            LogMessage(FormatHeader("Retracting Solutions"), LogLevel.High);

            foreach (string solutionName in solutionNames)
            {   
                if (!SolutionExistsOnServer(solutionName))
                {
                    LogWarning("Solution '" + solutionName + "' not installed in farm");
                }
                else
                {
                    SPSolution solution = SPFarm.Local.Solutions[solutionName];
                    if (SolutionIsDeployed(solutionName))
                    {
                        //run this section 3 times if a failure occurs
                        int runCount = 0;
                        bool success = false;
                        while (runCount < this.ParsedRetries)
                        {
                            if (solution.ContainsWebApplicationResource)
                            {
                              if (webApplicationNames.Length > 0)
                              {
                                Collection<SPWebApplication> retractFromWebAps = GetWebApplicationsFromNames(webApplicationNames);
                                LogMessage("Retracting '" + solution.Name + "' from webapps " + GetWebAppAsString(retractFromWebAps), LogLevel.Normal);
                                solution.Retract(DateTime.Now - TimeSpan.FromDays(1), retractFromWebAps);
                              }
                              else
                              {
                                Collection<SPWebApplication> retractFromWebAps = solution.DeployedWebApplications;
                                LogMessage("Retracting '" + solution.Name + "' from webapps " + GetWebAppAsString(retractFromWebAps), LogLevel.Normal);
                                solution.Retract(DateTime.Now - TimeSpan.FromDays(1), solution.DeployedWebApplications);
                              }
                            }
                            else
                            {
                                LogMessage("Retracting '" + solution.Name + "'...", LogLevel.Normal);
                                solution.Retract(DateTime.Now - TimeSpan.FromDays(1));
                            }

                            ExecAdmSvcJobs(false, false);

                            WaitForFinishedUndeployment(solutionName);

                            if (SolutionsSuccessfullyUndeployed(solutionName))
                            {
                                runCount = this.ParsedRetries + 1;
                                success = true;
                            }
                            else
                            {
                              LogWarning("Deployment of solution " + solutionName + " failed . Retry.");
                            }
                        }
                        if (!success)
                        {
                          exceptions.Add("Could not retract solution " + solutionName + "");
                        }
                    }
                    else
                    {
                      LogMessage("Skipping solution: '" + solutionName + "' is not deployed", LogLevel.Normal);
                    }
                }
            }

            HandleExceptions();

            LogMessage(FormatHeader("Retracting Solutions finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High);  

            LogMessage(FormatHeader("Deleting Solutions"), LogLevel.High);  
            foreach (string solutionName in solutionNames)
            {
                if (!SolutionExistsOnServer(solutionName))
                {
                    LogWarning("Solution '" + solutionName + "' not installed in farm");
                }
                else
                {
                    SPSolution solution = SPFarm.Local.Solutions[solutionName];

                    LogMessage("Deleting '" + solution.Name + "'...", LogLevel.Normal);
                    try
                    {
                        solution.Delete();
                    }
                    catch (Exception ex)
                    {
                        exceptions.Add("Could not delete solution '" + solution.Name + "' (" + ex.Message + ")");
                    }
                }
            }
            LogMessage(FormatHeader("Deleting Solutions finished"), LogLevel.High);
            LogMessage("", LogLevel.High);
        }

        private string GetWebAppAsString(Collection<SPWebApplication> retractFromWebApps)
        {
          string res = "";
          foreach (SPWebApplication retractFromWebApp in retractFromWebApps)
          {
            if (res != "")
            {
              res += ", ";
            }
            res += retractFromWebApp.Name;
          }
          return res;
        }

        private bool SolutionIsDeployed(string solutionName)
        {
          LogMessage("SolutionIsDeployed: Solution " + solutionName, LogLevel.Low);
          
          SPSolution solution = GetSPSolution(solutionName);
          if (solution != null)
          {
            LogMessage("SolutionIsDeployed: Solution " + solutionName + " DeploymentState " + solution.DeploymentState.ToString(), LogLevel.Low);
            LogMessage("SolutionIsDeployed: Solution " + solutionName + " Deployed " + solution.Deployed.ToString(), LogLevel.Low);
            LogMessage("SolutionIsDeployed: Solution " + solutionName + " LastOperation " + solution.LastOperationResult.ToString(), LogLevel.Low);
            LogMessage("SolutionIsDeployed: Solution " + solutionName + " Server " + solution.DeployedServers.Count.ToString(), LogLevel.Low);
            
            
            if (solution.Deployed)
            {
              return true;
            }
            return false;
          }
          else
          {
            LogMessage("SolutionIsDeployed: Solution " + solutionName + " not found", LogLevel.Low);
          }
          return true;
        }

        private bool SolutionsSuccessfullyUndeployed(string solutionName)
        {
          SPSolution solution = GetSPSolution(solutionName);
          if (solution != null)
          {
            LogMessage("SolutionsSuccessfullyUndeployed: Solution " + solutionName  + " " + solution.LastOperationResult.ToString(), LogLevel.Low);
            LogMessage("SolutionsSuccessfullyUndeployed: Solution Deployed " + solutionName + " " + solution.Deployed.ToString(), LogLevel.Low);
             
            if (solution.LastOperationResult == SPSolutionOperationResult.RetractionSucceeded)
            {
              LogMessage("SolutionsSuccessfullyUndeployed: Solution " + solutionName + "result is RetractionSucceeded", LogLevel.Low);
              return true;
            }
            if (solution.Deployed == false)
            {
              LogMessage("SolutionsSuccessfullyUndeployed: Solution " + solutionName + "not deployed", LogLevel.Low);
              return true;
            }
            return false;
          }
          else
          {
            LogMessage("SolutionsSuccessfullyUndeployed: Solution " + solutionName + " not found", LogLevel.Low);
          }
          return true;
        }
                
        public override void ValidateSystem()
        {
            LogMessage(FormatHeader("Validating deployment"), LogLevel.High);  

            // Check that all solutions are deleted
            foreach (string solutionName in solutionNames)
            {
                LogMessage("Validating solution '" + solutionName + "'...", LogLevel.Normal);
                SPSolution solution = SPFarm.Local.Solutions[solutionName];
                if (solution != null)
                {
                    exceptions.Add("Solution '" + solutionName + "' not removed from the farm");
                }
            }

            HandleExceptions();

            LogMessage(FormatHeader("Validating deployment finished successfully"), LogLevel.High);
            LogMessage("", LogLevel.High);
        }
    }
}
